#include <string>
#include <memory> // std::auto_ptr
#include <cstdlib> // std::exit
#include <iostream>
#include <odb/database.hxx>
#include <odb/mysql/database.hxx>
#include "student.hxx"
#include "student-odb.hxx"
#include <gflags/gflags.h>
// #include <odb/profile.hxx>

DEFINE_string(host, "127.0.0.1", "这是MySQL服务器地址");
DEFINE_int32(port, 3306, "这是MySQL服务器端口");
DEFINE_string(db, "TestDB", "数据库默认库名称");
DEFINE_string(user, "root", "这是MySQL用户名");
DEFINE_string(pswd, "zhi204071", "这是数据库密码");
DEFINE_string(cset, "utf8", "这是MySQL客户端字符集");
DEFINE_int32(max_pool, 3, "这是MySQL连接池最大的连接数量");

//新增数据
void insert_classes(odb::mysql::database& db)
{
    //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
    try
    {
        //3. 获取事务操作对象，开启事务
        odb::transaction trans(db.begin());
        //4. 数据操作
        Classes c1("一年级一班");
        Classes c2("一年级二班");
        db.persist(c1);
        db.persist(c2);
        //5. 提交事务
        trans.commit();
    }
    catch(std::exception& e)
    {
        std::cout << "插入数据时：" << e.what() << std::endl;
    }
}

//新增数据
void insert_student(odb::mysql::database& db)
{
    //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
    try
{
        //3. 获取事务操作对象，开启事务
        odb::transaction trans(db.begin());
        //4. 数据操作
        Student s1(1, "张三", 18, 1);
        Student s2(2, "李四", 20, 2);
        Student s3(3, "王五", 18, 1);
        Student s4(4, "赵六", 21, 2);
        Student s5(5, "田七", 17, 1);
        Student s6(6, "王麻子", 22, 2);
        Student s7(7, "韩老魔", 19, 1);
        Student s8(8, "张飞", 24, 2);
        db.persist(s1);
        db.persist(s2);
        db.persist(s3);
        db.persist(s4);
        db.persist(s5);
        db.persist(s6);
        db.persist(s7);
        db.persist(s8);
        //5. 提交事务
        trans.commit();
    }
    catch(std::exception& e)
    {
        std::cout << "插入数据时：" << e.what() << std::endl;
    }
}
//更新数据
void update_student(odb::mysql::database& db, Student& stu)
{
    //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
    try
    {
        //3. 获取事务操作对象，开启事务
        odb::transaction trans(db.begin());
        //4. 数据操作
        Student s1(1, "张三", 14, 1);
        //在进行数据更新的时候，如果直接进行更新会报错，odb中的用法是需要先查询数据再更新数据，是对查询出来的结果stu修改后更新，而不是s1
        db.update(stu);  
        //5. 提交事务
        trans.commit();
    }
    catch(std::exception& e)
    {
        std::cout << "更新数据时：" << e.what() << std::endl;
    }
}
//查询数据
Student select_student(odb::mysql::database& db)
{
    Student res;
    //数据的所有操作都会有可能导致程序的崩溃，所以需要捕捉异常防止程序的退出；
    try
    {
        //3. 获取事务操作对象，开启事务
        odb::transaction trans(db.begin());
        //4. 数据操作
        typedef odb::query<Student> query;
        typedef odb::result<Student> result;
        result r(db.query<Student>(query::name == "张三"));
        if(r.size() != 1)
        {
            std::cout << "数据量不对！" << std::endl;
            return Student();
        }
        res = *r.begin();
        //5. 提交事务
        trans.commit();
    }
    catch(std::exception& e)
    {
        std::cout << "查询数据时：" << e.what() << std::endl;
    }
    return res;
}
//移除数据
void remove_student(odb::mysql::database& db)
{
    try
    {
        typedef odb::query<Student> query;
        odb::transaction trans(db.begin());
        db.erase_query<Student>(query::classes_id == 2); //erase_query不用先查询数据再删除，可以根据过滤条件直接删除
        trans.commit();
    }
    catch(const std::exception& e)
    {
        std::cerr << "删除数据时："<< e.what() << std::endl;
    }
    
}

//多表联合查询
void select_classes_student(odb::mysql::database& db)
{
    try
    {
        typedef odb::query<classes_student> query;
        typedef odb::result<classes_student> result;
        odb::transaction trans(db.begin());
        result r(db.query<classes_student>(query::classes::name == "一年级一班"));
        for(auto it = r.begin(); it != r.end(); ++it)
        {
            std::cout << it->id << std::endl;
            std::cout << it->sn << std::endl;
            std::cout << it->name << std::endl;
            if(it->age)
            {
                //age有的时候才进行解引用输出
                std::cout << *it->age << std::endl;
            }
            std::cout << it->classes_name << std::endl;
            
        }
        //5. 提交事务
        trans.commit();
    }
    catch(const std::exception& e)
    {
        std::cerr << "查询数据时："<< e.what() << std::endl;
    }
}

//表中单字段查询
void select_all_student(odb::mysql::database& db)
{
    try
    { 
        // odb::profile p;  // 开始记录
        // {
            odb::transaction trans(db.begin());
            typedef odb::query<Student> query;
            typedef odb::result<all_name> result;
            //括号中的过滤条件 query::id == 1 最终就传给了#pragma db view query("select name from Student WHERE" + (?)) 中的 (?)
            result r(db.query<all_name>(query::id == 11)); 

            for(auto it = r.begin(); it != r.end(); ++it)
            {
                std::cout << it->name << std::endl;
            }
            //5. 提交事务
            trans.commit();
       // }
        // p.stop();
        // std::cout << p << std::endl;  // 输出包含SQL语句
    }
    catch(const std::exception& e)
    {
        std::cerr << "查询所有学生姓名数据时："<< e.what() << std::endl;
    }

}

int main(int argc, char* argv[])
{
    google::ParseCommandLineFlags(&argc, &argv, true);
    //1. 构造连接池工厂配置对象
    //注意这里的智能指针只能使用unique_ptr不能使用其他的智能指针，否则会报错
    std::unique_ptr<odb::mysql::connection_pool_factory> cpf(new odb::mysql::connection_pool_factory(FLAGS_max_pool, 0));
    //2. 构造数据库操作对象
    odb::mysql::database db(
        FLAGS_user,
        FLAGS_pswd,
        FLAGS_db, 
        FLAGS_host,
        FLAGS_port,
        "",
        FLAGS_cset,
        0,
        std::move(cpf)     //这里传参的时候要记得使用move进行资源的转移
    );
    //新增数据
    // insert_classes(db);
    // insert_student(db);

    //查询数据
    // auto stu = select_student(db);
    // std::cout << stu.sn() << std::endl;
    // std::cout << stu.name() << std::endl;
    // if(stu.age())
    // {
    //     //age有的时候才进行解引用输出
    //     std::cout << *stu.age() << std::endl;
    // }
    // std::cout << stu.classes_id() << std::endl;
    // stu.age(15);
    
    //更新数据
    // update_student(db, stu);

    //移除数据
    // remove_student(db);

    //多表联合查询
    // select_classes_student(db);

    //查询单个字段
    select_all_student(db);
    return 0;
}